/*    Copyright 2010 Tobias Marschall
 *
 *    This file is part of MoSDi.
 *
 *    MoSDi is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    MoSDi is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoSDi.  If not, see <http://www.gnu.org/licenses/>.
 */

package mosdi.util;

import java.util.Locale;

/** Helps with calculations in logarithmic domain. */
public class LogSpace {
	
	/** Given log x and log y, returns log (x+y) */
	public static double logAdd(double logX, double logY) {
		if (Double.isNaN(logX) || Double.isNaN(logY)) return Double.NaN;
		// possibly swap to ensure x>y
		if (logY > logX) {
			double temp = logX;
			logX = logY;
			logY = temp;
		}
		// if both are -inf, just return -inf
		if (logX==Double.NEGATIVE_INFINITY) return Double.NEGATIVE_INFINITY;
		
		// otherwise use log1p to compute result
		return logX + Math.log1p(Math.exp(logY-logX));
	}

	/** Given log x and log y, returns log (x-y) */
	public static double logSub(double logX, double logY) {
		if (Double.isNaN(logX) || Double.isNaN(logY)) return Double.NaN;
		if (logY > logX) return Double.NaN;
		// if both are -inf, just return -inf
		if (Double.isInfinite(logX) && (logX<0.0)) return Double.NEGATIVE_INFINITY;
		
		// otherwise use log1p to compute result
		return logX + Math.log1p(-Math.exp(logY-logX));
	}
	
	/** Given log(x), returns a string representation of x in scientific format. 
	 *  Returns correct results even when x<Double.MIN_VALUE or x>Double.MAX_VALUE. */
	public static String toString(double d) {
		if (Double.isNaN(d)) return String.valueOf(d);
		if (Double.isInfinite(d)) return String.valueOf(Math.exp(d));
		d/=Math.log(10.0);
		double exp;
		double factor;
		if (d<0.0) {
			exp=-Math.ceil(-d);
			factor=d-exp;
		} else {
			exp=Math.floor(d);
			factor=d-exp;
		}
		return String.format(Locale.US, "%.6fe%+d", Math.exp(factor*Math.log(10.0)), (int)exp);
	}
}
